package com.qf.index;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;

import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

public class Bibliometric {

    /**
     * bib文献计量源数据部分程序，主要包含：
     * 1. 首次生成总表，方法名：insertBibliometricTable，此方法在每次的数据更新后，一次性产生bib的基础数据集，存储时仅两个字段，一个是pmId，一个是包含所有数据的实体类，方便获取和使用。
     * 2. 实际投入使用后，当用户进行检索，前端获取所有的pmId号的集合（pmIdSet），调用方法：getNotPmIdSet，获取基础表中没有的pmId号集合返回：notPmIdSet
     * 3. 小葛的xml下载程序，把基础表中没有的pmId号全部给下载下来，并保存进pmArticleTempTable临时表（spider_pubmed_vip_extend_temp），此为临时表。
     * 4. 补充数据，方法名：addBibTable，此方法需要6张辅助表，包括期刊、mesh、词性表、STEM表、城市表、词数表等。将小葛下载的临时表数据补充到bib基础表中。
     * 5. pm数据导出，供贾谦数据分析使用，方法名：pmToText，将需要导出的pmId号集合、保存的路径以及bib基础表传入方法，方法保存后并返回导出的总条数。
     * 6. wos数据导出，方法名：wosToText，供贾谦数据分析使用。本方法按aid来导出，且可选择要全部还是只要wos的数据，依据的表是总表（summaryTable）。
     * 有以上方法，文献计量也实现了使用统一表的目标。
     * 贾新志 2025.03.08
     */

    public static void main(String[] args) throws SQLException {
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(format.format(new Date()) + " 程序开始了~~~");
        String newDataName = "scholar_25_02.";
        String summaryTable = newDataName + "unified_search_summary_0308";
        String pmArticleTempTable = newDataName + "spider_pubmed_vip_extend_temp";
        String jourMergeTable = newDataName + "basic_jour_merge";
        String wordAndCodeSetMapTable = newDataName + "word_and_code_set_map";
        String meshTable = newDataName + "help_mesh_tree_gcr_0615";
        String keyWordSumTable = newDataName + "key_word_sum_0124";
        String htLocationChinaTable = newDataName + "ht_location_china";
        String basicHospUnivDeptTable = newDataName + "basic_hosp_or_univ_dept";
        String bibliometricTable = newDataName + "bibliometric_pm_0308";

        int onePageNum = 1000;
        String limit = "";
//        String limit = " limit 1";

        String field = "drug_gene_pathway_disease`,`c1_au`,`rp_au`,`citations`,`pmId`,`doi`,`mesh`,`ab`,`keyword`,`title`,`ncbi_journal`,`year`,`is_cns`,`pt`,`is_china`,`nlmId`,`cn_country`,`info`,`ncbi_mesh`,`ncbi_subject`," +
                "`jcr_if`,`cite_score`,`zky_dalei`,`jcr_quartile`,`web_site`,`language`,`subject`,`ti_key`,`ab_key`,`kw_key`,`fund_list`,`times_cited`,`references`,`kw_plus";
        insertBibliometricTable(summaryTable, "id", field, bibliometricTable, limit);

        Set<Integer> pmIdSet = new HashSet<>();
        Set<Integer> notPmIdSet = getNotPmIdSet(bibliometricTable, pmIdSet);

        field = "pmid`,`keyword`,`article_title`,`ab_text`,`jour`,`pub_year`,`author_extend`,`pts`,`med_nlm_id`,`pub_date`,`doi`,`volume`,`issue`,`pages`,`lan`,`mesh_headingy";
        addBibTable(pmArticleTempTable, onePageNum,
                limit, "id", field, wordAndCodeSetMapTable, jourMergeTable,
                keyWordSumTable, meshTable, htLocationChinaTable, basicHospUnivDeptTable, bibliometricTable);

        String filePath = "C:\\临时文件\\2024_12\\wos8.txt";
        Set<Integer> aidSet = new HashSet<>();
        // 依据aid导出纯文本数据，filePath为保存的路径，isALL为true是全部都要，为false时，只导出wos的数据。
        int sum = wosToText(aidSet, filePath, summaryTable, false);

        // 依据pmId从bibliometricTable表中导出纯文本数据，number为返回的导出的条数。
        int number = pmToText(pmIdSet, filePath, bibliometricTable);

    }

    public static void addBibTable(String pmArticleTable, int onePageNum, String limit, String autoId, String field, String wordAndCodeSetMapTable, String jourMergeTable,
                                   String keyWordSumTable, String meshTable,
                                   String htLocationChinaTable,
                                   String basicHospUnivDeptTable, String bibliometricTable) throws SQLException {
        Map<String, En.JidInfo> nlmIdAndJidInfoMap = getNlmIdAndJidInfoMap(jourMergeTable, "id", "nc_nlmid_list`,`jcr_quartile`,`cite_score`,`jcr_if`,`zky_dalei`,`nc_nlm_title_abbreviation`,`ncbi_mesh`,`ncbi_subject");
        Map<String, Set<Integer>> wordAndCodeSetMap = Drug.getWordAndCodeSetMap(wordAndCodeSetMapTable, "id", "word`,`code_set", "");
        Map<String, String> md5Map = new ConcurrentHashMap<>();
        Map<String, String> reKwMap = cList.getReKwMap();
        Map<String, String> keyWordAndMeshMap = new ConcurrentHashMap<>();
        Map<String, String> originalAndMeshMap = new ConcurrentHashMap<>();
        ReadKw.readMesh(meshTable, "id", "meshName`,`entryTerms", true, 10, md5Map, keyWordAndMeshMap, originalAndMeshMap, reKwMap);
        Map<String, String> keyWordAndStandardMap = getKeyWordAndStandardMap(keyWordSumTable, keyWordAndMeshMap, md5Map, reKwMap, "id", "is_cn`,`sum`,`key_word");
        Map<String, String> languageMap = uSet.getLanguageMap();
        Set<String> chinaSet = cList.chinaSet();
        Set<String> citySet = new HashSet<>();
        Set<String> subjectSet = cList.getSubjectSet();
        Set<String> provCitySet = Dept.getProvCitySet(htLocationChinaTable, "id", "prov`,`name", citySet);
        Map<String, String> standardMap = new ConcurrentHashMap<>();
        standardMap.put("department medical", "Department Medical");
        Map<String, Set<String>> deptAndInfoSetMap = new ConcurrentHashMap<>();
        Map<String, Integer> deptAndSumMap = new ConcurrentHashMap<>();
        Map<String, Set<String>> unitAndInfoSetMap = new ConcurrentHashMap<>();
        Map<String, Integer> unitAndSumMap = new ConcurrentHashMap<>();
        Map<String, String> countryMap = cList.getCountryMap();
        Set<String> acadSet = uSet.getAcadSet();
        Set<String> centerSet = uSet.getCenterSet();
        Set<String> collSet = uSet.getCollSet();
        Set<String> hospSet = uSet.getHospSet();
        Set<String> univSet = uSet.getUnivSet();
        Set<String> removeSet = uSet.getRemoveSet();
        Set<String> dellSet = uSet.getDellSet();
        Set<String> toDeptSet = uSet.toDept();
        Map<String, En.FuDanStemEdu> fuDanStemEduMap = Guidelines.getFuDanStemEduMap(basicHospUnivDeptTable, "id", "info`,`fudan`,`stem`,`edu");

        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + pmArticleTable);
        String s = "<p> <strong class=\"sub-title\"> Keywords: </strong>";
        if (tableNum.get(0).getInt("count") < 1) {
            return;
        }
        Set<String> pinYinSet = PmUnit.getPinYinSet();
        int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + pmArticleTable).intValue();
        Collection<Integer> tableList = new ArrayList<>();
        NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
        for (Integer startId : tableList) {
            List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + pmArticleTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
            System.out.println("\033[31;1m" + format.format(new Date()) + " 读表现在开始：" + startId + " 表名：" + pmArticleTable + Utils.printMemoryStr() + "\033[0m");
            for (Entity entry : tableData) {
                Integer pmId = entry.getInt("pmid");
                String keyword = Utils.getStrField(entry, "keyword");
                keyword = keyword.startsWith(s) && keyword.endsWith("</p>") ? keyword.substring(s.length(), keyword.length() - "</p>".length()).trim() : keyword;
                String title = Utils.getStrField(entry, "article_title");
                String abText = Utils.getStrField(entry, "ab_text");
                abText = abText.endsWith("</p>") ? abText.substring(0, abText.length() - "</p>".length()).trim() : abText;
                abText = abText.startsWith("<p>") ? abText.substring("<p>".length()).trim() : abText;
                abText = abText.replace("<strong class=\"sub-title\">", " ").replace(" </strong>", " ").replace("</p><p>", " ").replaceAll(" +", " ").trim();
                String journal = Utils.getStrField(entry, "jour");
                String pubYear = Utils.getStrField(entry, "pub_year");
                int year = (pubYear.trim().length() == 4 && NumberUtil.isInteger(pubYear.trim())) ? Integer.parseInt(pubYear.trim()) : 1900;
                String authorExtend = entry.getStr("author_extend");
                String pts = entry.getStr("pts");
                List<En.PubType> pubTypes = JSONObject.parseArray((null == pts || pts.isEmpty()) ? "[]" : pts, En.PubType.class);
                Set<String> ptSet = new HashSet<>();
                for (En.PubType pubType : pubTypes) {
                    String typeTitle = pubType.getTitle().trim();
                    if (!typeTitle.isEmpty()) {
                        ptSet.add(typeTitle);
                    }
                }
                String nlmId = Utils.getStrField(entry, "med_nlm_id").toUpperCase().trim();
                En.JidInfo jidInfo = nlmIdAndJidInfoMap.containsKey(nlmId) ? nlmIdAndJidInfoMap.get(nlmId) : new En.JidInfo();
                int isCns = Utils.is6DaJournals(nlmId, journal, "", "");
                String pubDate = Utils.getStrField(entry, "pub_date").replace("--", "-").trim();
                pubDate = pubDate.endsWith("-") ? pubDate.substring(0, pubDate.length() - 1) : pubDate;
                String di = Utils.dellEnd(Utils.getStrField(entry, "doi"));
                String volume = Utils.getStrField(entry, "volume");
                String issue = Utils.getStrField(entry, "issue");
                String pages = Utils.getStrField(entry, "pages");
                di = di.endsWith(".") ? di.substring(0, di.length() - 1) : di;
                String info = (journal + ". " + (pubDate + ";") + volume + (issue.isEmpty() ? ":" : "(" + issue + "):") + (pages + ". ") + (di.length() > 6 ? ("doi: " + di + ". ") : ""));
                List<En.CategoryAndInfo> categoryAndInfoList = Drug.getDrug(title, keyword, abText, wordAndCodeSetMap);
                List<En.C1Auth> pmC1ListSort = Utils.getPmC1ListSort(authorExtend, pinYinSet);

                String language = Utils.getStrField(entry, "lan");
                String[] split = language.replace(";-;", "ĺļ").replace("; ", "ĺļ").replace(";", "ĺļ").split("ĺļ");
                List<String> languageList = new ArrayList<>();
                for (String p : split) {
                    if (p.trim().length() > 2) {
                        languageList.add(languageMap.getOrDefault(p.toLowerCase().trim(), p.trim()));
                    }
                }

                Guidelines.setC1List(pmC1ListSort, standardMap, acadSet, centerSet, collSet, hospSet, univSet, removeSet, countryMap, deptAndSumMap, dellSet,
                        chinaSet, provCitySet, citySet, subjectSet, deptAndInfoSetMap, unitAndInfoSetMap, unitAndSumMap, toDeptSet, fuDanStemEduMap);

                String c1Au = JSONUtil.toJsonStr(pmC1ListSort);
                List<En.SortAuthAndUnit> sortAuthList = c1AuToSortAuth(c1Au);
                List<En.AuthListAndInfo> c1List = c1AuToC1List(c1Au);

                List<String> countryList = new ArrayList<>();
                for (En.C1Auth c1Auth : pmC1ListSort) {
                    String country = c1Auth.getCountry();
                    if (country.length() > 1) {
                        countryList.add(country);
                    }
                }

                Set<List<String>> kwSetList = new HashSet<>();
                if (!keyword.isEmpty()) {
                    List<String> kwList = new ArrayList<>(Arrays.asList(keyword.split("; ")));
                    ReadKw.addSetList(originalAndMeshMap, kwList, kwSetList, false);
                }

                Set<List<String>> tiSetList = new HashSet<>();
                Set<String> tiKeySet = title.length() > 4 ? ReadKw.getTiKey(title, keyWordAndStandardMap) : new HashSet<>();
                ReadKw.addSetList(originalAndMeshMap, new ArrayList<>(tiKeySet), tiSetList, true);
                ReadKw.addAbbSet(title, tiSetList);

                Set<List<String>> abSetList = new HashSet<>();
                Set<String> abKeySet = abText.length() > 10 ? ReadKw.getAbKey(abText, keyWordAndStandardMap) : new HashSet<>();
                ReadKw.addSetList(originalAndMeshMap, new ArrayList<>(abKeySet), abSetList, true);
                ReadKw.addAbbSet(abText, abSetList);

                En.TableData table = new En.TableData();
                table.setPmId(pmId);
                table.setDi(di);
                table.setMesh(Utils.getStrField(entry, "mesh_headingy").replace(";", "; ").replace("  ", " ").trim());
                table.setAbText(abText);
                table.setKeyword(keyword);
                table.setTitle(title);
                table.setJournal(journal);
                table.setYear(year);
                table.setIsCns(isCns);
                table.setPtSet(ptSet);
                table.setNlmId(nlmId.toUpperCase().trim());
                table.setDrugGenePathwayDiseaseList(categoryAndInfoList);
                table.setInfo(Fusion.reInfo(info));
                table.setIsChina(countryList.contains("中国") ? 1 : 0);
                table.setCountryList(countryList);
                table.setSortAuthList(sortAuthList);
                table.setNcbiMesh(jidInfo.getNcbiMesh());
                table.setNcbiSubject(strToList(jidInfo.getNcbiSubject()));
                table.setJcrIf(jidInfo.getJcrIf());
                table.setCiteScore(jidInfo.getCiteScore());
                table.setZkyDaLei(jidInfo.getZkyDaLei());
                table.setJcrQuartile(jidInfo.getJcrQuartile());
                table.setLan(CollUtil.join(languageList, "; "));
                table.setExtractTitle(tiSetList);
                table.setExtractAbstract(abSetList);
                table.setKeyWordSet(kwSetList);
                table.setC1List(c1List);
                try {
                    Db.use().insert(Entity.create(bibliometricTable).set("info", JSONUtil.toJsonStr(table)).set("pmid", pmId));
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        nlmIdAndJidInfoMap.clear();
        wordAndCodeSetMap.clear();
        md5Map.clear();
        reKwMap.clear();
        keyWordAndMeshMap.clear();
        originalAndMeshMap.clear();
        keyWordAndStandardMap.clear();
        languageMap.clear();
        chinaSet.clear();
        citySet.clear();
        subjectSet.clear();
        provCitySet.clear();
        standardMap.clear();
        deptAndInfoSetMap.clear();
        deptAndSumMap.clear();
        unitAndInfoSetMap.clear();
        unitAndSumMap.clear();
        countryMap.clear();
        acadSet.clear();
        centerSet.clear();
        collSet.clear();
        hospSet.clear();
        univSet.clear();
        removeSet.clear();
        dellSet.clear();
        toDeptSet.clear();
        fuDanStemEduMap.clear();
    }

    public static List<String> strToList(String str) {
        List<String> list = new ArrayList<>();
        if (null != str && !str.trim().isEmpty()) {
            String[] split = str.split("; ");
            for (String s : split) {
                if (!s.trim().isEmpty()) {
                    list.add(s);
                }
            }
        }
        return list;
    }

    public static Map<String, String> getKeyWordAndStandardMap(String keyWordSumTable, Map<String, String> keyWordAndMeshMap, Map<String, String> md5Map, Map<String, String> reKwMap, String autoId, String field) throws SQLException {
        int onePageNum = 1000;
        Map<String, String> keyWordAndStandardMap = new ConcurrentHashMap<>();
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + keyWordSumTable);
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + keyWordSumTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            for (Integer startId : tableList) {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + keyWordSumTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1));
                for (Entity entry : tableData) {
                    int isCn = Utils.getIntField(entry, "is_cn");
                    int sum = Utils.getIntField(entry, "sum");
                    String keyWord = Utils.getStrField(entry, "key_word");
                    if (isCn == 0 && keyWord.contains("\"]")) {
                        List<String> keyWordList = JSONObject.parseArray(keyWord, String.class);
                        if (sum > 50 || (keyWordList.size() == 2 && sum > 10) || (keyWordList.size() == 3 && sum > 1)) {
                            String kw = ReadKw.reRemaining(Utils.reKw(reKwMap, keyWordList.get(0)));
                            String standard = keyWordAndMeshMap.getOrDefault(keyWordList.get(0), kw);
                            En.InfoAndStandard infoAndStandard = ReadKw.getInfoAndStandard(kw, standard, md5Map, reKwMap);
                            keyWordAndStandardMap.put(infoAndStandard.getInfo(), infoAndStandard.getStandard());
                        }
                    } else if (isCn == 1 && sum > 30 && keyWord.length() > 1) {
                        En.InfoAndStandard infoAndStandard = ReadKw.getInfoAndStandard(keyWord, keyWord, md5Map, reKwMap);
                        keyWordAndStandardMap.put(infoAndStandard.getInfo(), infoAndStandard.getStandard());
                    }
                }
            }
        }
        return keyWordAndStandardMap;
    }

    public static Map<String, En.JidInfo> getNlmIdAndJidInfoMap(String jourMergeTable, String autoId, String field) throws SQLException {
        int onePageNum = 1000;
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + jourMergeTable);
        Map<String, En.JidInfo> nlmIdAndJidInfoMap = new HashMap<>();
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + jourMergeTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            for (Integer startId : tableList) {
                List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + jourMergeTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1));
                for (Entity entry : tableData) {
                    String nlmIds = Utils.getStrField(entry, "nc_nlmid_list");
                    if (nlmIds.contains("\"]")) {
                        List<String> nlmIdList = JSONObject.parseArray(nlmIds, String.class);
                        if (!nlmIdList.isEmpty()) {
                            En.JidInfo jidInfo = new En.JidInfo();
                            jidInfo.setNlmId(nlmIdList.get(0).toUpperCase().trim());
                            int jid = Utils.getIntField(entry, autoId);
                            String jcrQuartile = Utils.getStrField(entry, "jcr_quartile").toUpperCase().replace("Q", "").replace(" ", "").trim();
                            jidInfo.setJcrQuartile(NumberUtil.isInteger(jcrQuartile) ? Integer.parseInt(jcrQuartile) : 0);
                            jidInfo.setCiteScore(Utils.getDouField(entry, "cite_score"));
                            jidInfo.setJcrIf(Utils.getDouField(entry, "jcr_if"));
                            jidInfo.setZkyDaLei(Utils.getIntField(entry, "zky_dalei"));
                            jidInfo.setJid(jid);
                            jidInfo.setNcbiJournal(Utils.getStrField(entry, "nc_nlm_title_abbreviation").replace("\"", ""));
                            String meshs = Utils.getStrField(entry, "ncbi_mesh").replace("\"", "");
                            if (!meshs.isEmpty()) {
                                List<String> list = Arrays.asList(meshs.split("Łł"));
                                jidInfo.setNcbiMesh(CollUtil.join(list, "; "));
                            }
                            String subjects = Utils.getStrField(entry, "ncbi_subject");
                            if (subjects.contains("\"]")) {
                                List<String> list = JSONObject.parseArray(subjects, String.class);
                                jidInfo.setNcbiSubject(CollUtil.join(list, "; ").replace("\"", ""));
                            }
                            nlmIdAndJidInfoMap.put(jidInfo.getNlmId(), jidInfo);
                        }
                    }
                }
            }
        }
        return nlmIdAndJidInfoMap;
    }

    public static void insertBibliometricTable(String summaryTable, String autoId, String field, String bibliometricTable, String limit) throws SQLException {
        List<Entity> tableNum = Db.use().query("select count(1) as count from " + summaryTable);
        int onePageNum = 1000;
        Set<Integer> pmIdSet = new ConcurrentHashSet<>();
        Map<String, String> standardMap = new ConcurrentHashMap<>();
        Db.use().execute("truncate table " + bibliometricTable);
        if (tableNum.get(0).getInt("count") > 0) {
            int tableMax = Db.use().queryNumber("select max(" + autoId + ") from " + summaryTable).intValue();
            Collection<Integer> tableList = new ArrayList<>();
            NumberUtil.appendRange(0, tableMax + onePageNum, onePageNum, tableList);
            tableList.parallelStream().forEach(startId -> {
                try {
                    List<Entity> tableData = Db.use().query("select `" + autoId + "`,`" + field + "` from " + summaryTable + " where " + autoId + " between " + startId + " and " + (startId + onePageNum - 1) + limit);
                    for (Entity entry : tableData) {
                        Integer pmId = entry.getInt("pmId");
                        if (null != pmId && pmId > 0 && !pmIdSet.contains(pmId)) {
                            try {
                                Db.use().insert(Entity.create(bibliometricTable).set("info", JSONUtil.toJsonStr(getTableData(entry, standardMap))).set("pmid", pmId));
                                pmIdSet.add(pmId);
                            } catch (SQLException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            });
        }
        pmIdSet.clear();
        standardMap.clear();
    }

    public static En.TableData getTableData(Entity entry, Map<String, String> standardMap) {
        En.TableData table = new En.TableData();
        String drug = entry.getStr("drug_gene_pathway_disease");
        List<En.CategoryAndInfo> drugList = (null != drug && drug.contains("infoList")) ? JSONObject.parseArray(drug, En.CategoryAndInfo.class) : new ArrayList<>();
        String c1Au = entry.getStr("c1_au");
        List<En.SortAuthAndUnit> sortAuthList = c1AuToSortAuth(c1Au);
        List<En.AuthListAndInfo> c1List = c1AuToC1List(c1Au);
        List<En.AuthListAndInfo> rpList = c1AuToC1List(entry.getStr("rp_au"));
        String citation = Utils.getStrField(entry, "citations");
        List<En.TiAuJ9> citationList = citation.contains("pmId") ? JSONObject.parseArray(citation, En.TiAuJ9.class) : new ArrayList<>();
        table.setPmId(Utils.getIntField(entry, "pmId"));
        table.setDi(Utils.getStrField(entry, "doi"));
        table.setMesh(JSONUtil.toJsonStr(getMeshSet(Utils.getStrField(entry, "mesh"), standardMap).getMesh()));
        table.setAbText(Utils.getStrField(entry, "ab"));
        table.setKeyword(Utils.getStrField(entry, "keyword"));
        table.setTitle(Utils.getStrField(entry, "title"));
        table.setJournal(getStandard(Utils.getStrField(entry, "ncbi_journal"), standardMap));
        table.setYear(Utils.getIntField(entry, "year"));
        table.setIsCns(Utils.getIntField(entry, "is_cns"));
        table.setPtSet(getPtSet(Utils.getFieldList(entry, "pt"), standardMap));
        table.setIsChina(Utils.getIntField(entry, "is_china"));
        table.setNlmId(Utils.getStrField(entry, "nlmId"));
        table.setCountryList(changeStrList(Utils.getFieldList(entry, "cn_country"), standardMap));
        table.setSortAuthList(changeSortAuthList(sortAuthList, standardMap));
        table.setDrugGenePathwayDiseaseList(changeDrugList(drugList, standardMap));
        table.setInfo(Utils.getStrField(entry, "info"));
        table.setNcbiMesh(Utils.getStrField(entry, "ncbi_mesh"));
        table.setNcbiSubject(changeStrList(Utils.getStrToList(entry, "ncbi_subject"), standardMap));
        table.setJcrIf(Utils.getDouField(entry, "jcr_if"));
        table.setCiteScore(Utils.getDouField(entry, "cite_score"));
        table.setZkyDaLei(Utils.getIntField(entry, "zky_dalei"));
        table.setJcrQuartile(Utils.getIntField(entry, "jcr_quartile"));
        table.setWebSite(getStandard(Utils.getStrField(entry, "web_site"), standardMap));
        table.setLan(getStandard(Utils.getStrField(entry, "language"), standardMap));
        table.setWc(Utils.getStrField(entry, "subject"));
        table.setExtractTitle(getKeyWordSet("ti_key", entry, standardMap));
        table.setExtractAbstract(getKeyWordSet("ab_key", entry, standardMap));
        table.setKeyWordSet(getKeyWordSet("kw_key", entry, standardMap));
        table.setFundList(changeStrList(Utils.getFieldList(entry, "fund_list"), standardMap));
        table.setRpList(rpList);
        table.setC1List(c1List);
        table.setCitationList(citationList);
        table.setTotal(Utils.getIntField(entry, "times_cited"));
        table.setReferenceList(changeStrList(Utils.getStrToList(entry, "references"), standardMap));
        table.setKwPlusList(changeStrList(Utils.getFieldList(entry, "kw_plus"), standardMap));
        return table;
    }

    public static List<En.AuthListAndInfo> c1AuToC1List(String c1Aus) {
        List<En.AuthListAndInfo> c1List = new ArrayList<>();
        if (null != c1Aus && c1Aus.contains("sort")) {
            List<En.C1Auth> c1sList = JSONObject.parseArray(c1Aus, En.C1Auth.class);
            for (En.C1Auth c1Auth : c1sList) {
                En.AuthListAndInfo authListAndInfo = new En.AuthListAndInfo();
                authListAndInfo.setInfo(c1Auth.getInfo());
                authListAndInfo.setFull(c1Auth.getFull());
                authListAndInfo.setAuthList(c1Auth.getAuthList());
                Set<String> deptSet = c1Auth.getDeptSet();
                String dept1 = "";
                String dept2 = "";
                for (String dept : deptSet) {
                    dept1 = dept;
                    String l = dept.toLowerCase().trim();
                    if (l.startsWith("dept ") || l.contains("department")) {
                        dept2 = dept;
                    }
                }
                // 在c1Auth中，部门是集合，所以优先拿含有"dept "或"department"的部门信息，无时再使用其它的。
                authListAndInfo.setDept(dept2.isEmpty() ? dept1 : dept2);
                c1List.add(authListAndInfo);
            }
        }
        return c1List;
    }

    public static List<En.SortAuthAndUnit> c1AuToSortAuth(String c1Aus) {
        List<En.SortAuthAndUnit> sortAuthList = new ArrayList<>();
        if (null != c1Aus && c1Aus.contains("sort")) {
            Map<String, List<En.OriginalAndStandards>> authAndInfoListMap = new LinkedHashMap<>();
            List<En.C1Auth> c1List = JSONObject.parseArray(c1Aus, En.C1Auth.class);
            for (En.C1Auth c1Auth : c1List) {
                List<String> authList = c1Auth.getAuthList();
                String info = c1Auth.getInfo();
                String full = c1Auth.getFull();
                En.OriginalAndStandards originalAndStandards = new En.OriginalAndStandards();
                originalAndStandards.setOriginal(full.isEmpty() ? info : full);
                originalAndStandards.setStandards(new ArrayList<>(c1Auth.getUnitSet()));
                for (String auth : authList) {
                    authAndInfoListMap.compute(auth, (k, v) -> {
                        if (v == null) {
                            v = new ArrayList<>();
                        }
                        v.add(originalAndStandards);
                        return v;
                    });
                }
            }
            for (Map.Entry<String, List<En.OriginalAndStandards>> entry : authAndInfoListMap.entrySet()) {
                En.OriginalAndStandard auths = new En.OriginalAndStandard();
                auths.setOriginal(entry.getKey());
                En.SortAuthAndUnit sortAuthAndUnit = new En.SortAuthAndUnit();
                sortAuthAndUnit.setAuths(auths);
                sortAuthAndUnit.setUnitList(entry.getValue());
                sortAuthList.add(sortAuthAndUnit);
            }
        }
        return sortAuthList;
    }

    public static List<En.SortAuthAndUnit> changeSortAuthList(List<En.SortAuthAndUnit> sortAuthList, Map<String, String> standardMap) {
        // 将作者和单位，修改为首字母大写的形式。
        List<En.SortAuthAndUnit> sortAuthList2 = new ArrayList<>();
        for (En.SortAuthAndUnit sortAuthAndUnit : sortAuthList) {
            En.SortAuthAndUnit sortAuthAndUnit2 = new En.SortAuthAndUnit();
            List<En.OriginalAndStandards> unitList2 = new ArrayList<>();
            List<En.OriginalAndStandards> unitList = sortAuthAndUnit.getUnitList();
            for (En.OriginalAndStandards originalAndStandards : unitList) {
                En.OriginalAndStandards originalAndStandards2 = new En.OriginalAndStandards();
                List<String> standards2 = new ArrayList<>();
                List<String> standards = originalAndStandards.getStandards();
                for (String standard : standards) {
                    standards2.add(getStandard(standard, standardMap));
                }
                originalAndStandards2.setOriginal(originalAndStandards.getOriginal());
                originalAndStandards2.setStandards(standards2);
                unitList2.add(originalAndStandards2);
            }
            sortAuthAndUnit2.setUnitList(unitList2);
            En.OriginalAndStandard auths2 = new En.OriginalAndStandard();
            En.OriginalAndStandard auths = sortAuthAndUnit.getAuths();
            auths2.setOriginal(getStandard(auths.getOriginal(), standardMap));
            auths2.setStandard(getStandard(auths.getStandard(), standardMap));
            sortAuthAndUnit2.setAuths(auths2);
            sortAuthList2.add(sortAuthAndUnit2);
        }
        return sortAuthList2;
    }

    public static List<En.CategoryAndInfo> changeDrugList(List<En.CategoryAndInfo> drugList, Map<String, String> standardMap) {
        // 将疾病、药物、基因、通路，修改为首字母大写的形式。
        List<En.CategoryAndInfo> drugList2 = new ArrayList<>();
        for (En.CategoryAndInfo categoryAndInfo : drugList) {
            List<En.CodeAndWordList> infoList2 = new ArrayList<>();
            List<En.CodeAndWordList> infoList = categoryAndInfo.getInfoList();
            for (En.CodeAndWordList codeAndWordList : infoList) {
                List<String> wordList2 = new ArrayList<>();
                List<String> wordList = codeAndWordList.getWordList();
                for (String word : wordList) {
                    wordList2.add(getStandard(word, standardMap));
                }
                En.CodeAndWordList codeAndWordList2 = new En.CodeAndWordList();
                codeAndWordList2.setWordList(wordList2);
                codeAndWordList2.setCode(codeAndWordList.getCode());
                infoList2.add(codeAndWordList2);
            }
            En.CategoryAndInfo categoryAndInfo2 = new En.CategoryAndInfo();
            categoryAndInfo2.setInfoList(infoList2);
            categoryAndInfo2.setCategory(categoryAndInfo.getCategory());
            drugList2.add(categoryAndInfo2);
        }
        return drugList2;
    }

    public static List<String> changeStrList(List<String> cnCountry, Map<String, String> standardMap) {
        List<String> countryList = new ArrayList<>();
        for (String country : cnCountry) {
            countryList.add(getStandard(country, standardMap));
        }
        return countryList;
    }

    public static Set<String> getPtSet(List<String> ptList, Map<String, String> standardMap) {
        Set<String> ptSet = new HashSet<>();
        for (String pt : ptList) {
            ptSet.add(getStandard(pt, standardMap));
        }
        return ptSet;
    }

    public static Set<List<String>> getKeyWordSet(String field, Entity entry, Map<String, String> standardMap) {
        // 将关键词，修改为首字母大写的形式。
        String str = entry.getStr(field);
        Set<List<String>> set = new HashSet<>();
        if (null != str && str.contains("\"]]")) {
            List<String> lists = JSONObject.parseArray(str, String.class);
            for (String s : lists) {
                if (null != s && s.contains("\"]")) {
                    List<String> list = JSONObject.parseArray(s, String.class);
                    List<String> result = new ArrayList<>();
                    for (String t : list) {
                        result.add(getStandard(t, standardMap));
                    }
                    set.add(result);
                }
            }
        }
        return set;
    }

    public static En.MeSH getMeshSet(String mesh, Map<String, String> standardMap) {
        // 将mesh字符串转换成主题词集合（mhSet）和副主题词集合（shSet）两部分。
        Set<String> shSet = new HashSet<>();
        Set<String> mhSet = new HashSet<>();
        if (null != mesh && mesh.length() > 3) {
            String[] split = mesh.split(";");
            for (String mh : split) {
                String[] mhs = mh.split("\\|");
                mhSet.add(getStandard(mhs[0], standardMap));
                if (mhs.length == 2) {
                    String[] shs = mhs[1].split("/");
                    for (String sh : shs) {
                        sh = sh.startsWith("*") ? sh.substring(1) : sh;
                        shSet.add(getStandard(sh, standardMap));
                    }
                }
            }
        }
        En.MeSH meSh = new En.MeSH();
        meSh.setMesh(mesh);
        meSh.setMhSet(mhSet);
        meSh.setShSet(shSet);
        return meSh;
    }

    public static String getStandard(String keyWord, Map<String, String> standardMap) {
        if (null == keyWord || keyWord.isEmpty()) {
            return "";
        }
        String lowercase = keyWord.toLowerCase().trim();
        if (!standardMap.containsKey(lowercase)) {
            standardMap.put(lowercase, toUpperCase(keyWord));
        }
        return standardMap.get(lowercase);
    }

    // 首字母大写，后面的小写，如果不含空格，全部是大写，或者首字母之后含有大写，可以认定为缩写词，缩写词是不改变大小写的。如果不含空格且仅有一位或二位也直接返回。
    public static String toUpperCase(String keyWord) {
        if (!keyWord.contains(" ") && (keyWord.length() < 3 || !Pattern.compile("[a-z]").matcher(keyWord).find() || Pattern.compile("[A-Z]").matcher(keyWord.substring(1)).find())) {
            return keyWord;
        }
        StringBuilder r = new StringBuilder();
        String[] split = keyWord.replace("-", "łł ").split(" ");
        for (String s : split) {
            if (!s.isEmpty()) {
                r.append(s.substring(0, 1).toUpperCase()).append(s.substring(1).toLowerCase()).append(" ");
            }
        }
        return r.toString().replace("łł ", "-").trim();
    }

    public static Set<Integer> getNotPmIdSet(String bibliometricPmTable, Set<Integer> pmIdSet) throws SQLException {
        // 查看现有的表中，是否全部包含用户检索的pmId号集合，如果全部包含，pmIdSet为空，如果有不包含的pmIdSet内的pmId号，即为需要即时下载的pmId集合。
        Set<Integer> notPmIdSet = new HashSet<>(pmIdSet);
        List<Entity> tableData = Db.use().query("select `pmId` from " + bibliometricPmTable + " where pmId in(" + CollUtil.join(pmIdSet, ",") + ");");
        for (Entity entry : tableData) {
            Integer pmId = entry.getInt("pmId");
            if (null != pmId && pmId > 0) {
                notPmIdSet.remove(pmId);
            }
        }
        return notPmIdSet;
    }

    public static List<String> getPmAuthList(String authorExtend, Set<String> mailSet) {
        List<String> authorList = new ArrayList<>();
        if (null != authorExtend && authorExtend.contains("Affiliation")) {
            JSONArray objects = JSONUtil.parseArray(authorExtend);
            for (Object object : objects) {
                JSON parse = JSONUtil.parse(object);
                String lastName = null != parse.getByPath("LastName") ? parse.getByPath("LastName").toString().trim() : "";
                String foreName = null != parse.getByPath("ForeName") ? parse.getByPath("ForeName").toString().trim() : "";
                if ("-".equals(foreName)) {
                    authorList.add(lastName);
                } else {
                    if ((lastName + ", " + foreName).length() > 3) {
                        authorList.add(lastName + ", " + foreName);
                    }
                }
                String affiliation = parse.getByPath("Affiliation").toString();
                List<String> infoList = com.alibaba.fastjson.JSON.parseArray(affiliation, String.class);
                for (String info : infoList) {
                    String[] split = info.split(" ");
                    for (String mail : split) {
                        if (Utils.fuHeiMail(mail)) {
                            mailSet.add(Utils.dellEnd(mail));
                        }
                    }
                }
            }
        }
        return authorList;
    }

    public static int getIsRetraction(String ti, Set<String> ptSet) {
        if (null != ti && !ti.isEmpty()) {
            ti = ti.toLowerCase().trim();
            if (ti.startsWith("retraction") || ti.startsWith("retracted") || ti.contains("撤稿声明") || ti.contains("被撤回的出版物")) {
                return 1;
            }
        }
        for (String pt : ptSet) {
            pt = pt.toLowerCase().trim();
            if (pt.contains("withdrawn publication") || "retraction".equals(pt) || pt.contains("retracted publication") || pt.contains("retraction of publication")) {
                return 1;
            }
        }
        return 0;
    }

    public static int wosToText(Set<Integer> aidSet, String filePath, String summaryTable, boolean isAll) throws SQLException {
        // 基于summaryTable导出的纯文本格式的数据，并返回导出的条数。参数：filePath为保存的路径。aidSet为需要导出的aid号的集合，该集合有可能大于一万篇，所以拆为1000条一次。全部都要（isAll，即知网、WOS等都要）时为true，只要wos时为false。
        Map<String, String> cnEnCountryMap = cList.getCnEnCountryMap();
        FileUtil.appendUtf8String("FN Clarivate Analytics Web of Science" + System.lineSeparator(), filePath);
        FileUtil.appendUtf8String("VR 1.0" + System.lineSeparator(), filePath);
        Set<Integer> sumSet = new HashSet<>();
        List<Integer> aidList = new ArrayList<>();
        for (Integer aid : aidSet) {
            aidList.add(aid);
            if (aidList.size() == 1000) {
                readWos(aidList, filePath, summaryTable, cnEnCountryMap, sumSet, isAll);
                aidList.clear();
            }
        }
        if (!aidList.isEmpty()) {
            readWos(aidList, filePath, summaryTable, cnEnCountryMap, sumSet, isAll);
        }
        FileUtil.appendUtf8String("EF", filePath);
        return sumSet.size();
    }

    public static void readWos(List<Integer> aidList, String filePath, String summaryTable, Map<String, String> cnEnCountryMap, Set<Integer> sumSet, boolean isAll) throws SQLException {
        String field = "`kw_plus`,`keyword`,`doi`,`c1_au`,`auth`,`rp_au`,`ab`,`references`,`times_cited`,`title`,`subject`,`year`,`language`,`journal`,`pt`,`fund_list`,`cn_country`,`pmId`,`aid`,`ut`";
        List<Entity> dataTable = Db.use().query("select " + field + " from " + summaryTable + " where aid in(" + CollUtil.join(aidList, ",") + ");");
        for (Entity entry : dataTable) {
            Integer aid = entry.getInt("aid");
            String ut = Utils.getStrField(entry, "ut");
            if (null != aid && aid > 0 && (isAll || !ut.isEmpty())) {
                saveField(filePath, listToJoin(Utils.getFieldList(entry, "kw_plus")), "ID");
                saveField(filePath, listToJoin(Utils.getFieldList(entry, "keyword")), "DE");
                saveField(filePath, Utils.getStrField(entry, "doi"), "DI");
                saveField(filePath, c1AuToJoin(Bibliometric.c1AuToC1List(entry.getStr("c1_au")), new ArrayList<>()), "C1");
                saveField(filePath, listToJoin(Utils.getFieldList(entry, "auth")), "AU");
                saveField(filePath, rpAuToJoin(Bibliometric.c1AuToC1List(entry.getStr("rp_au"))), "RP");
                saveField(filePath, Utils.getStrField(entry, "ab"), "AB");
                saveField(filePath, Utils.getStrField(entry, "references"), "CR");
                saveField(filePath, Integer.toString(Utils.getIntField(entry, "times_cited")), "TC");
                saveField(filePath, Utils.getStrField(entry, "title"), "TI");
                saveField(filePath, listToJoin(Utils.getFieldList(entry, "subject")), "WC");
                saveField(filePath, Integer.toString(Utils.getIntField(entry, "year")), "PY");
                saveField(filePath, Utils.getStrField(entry, "language"), "LA");
                saveField(filePath, Utils.getStrField(entry, "journal"), "SO");
                saveField(filePath, listToJoin(Utils.getFieldList(entry, "pt")), "DT");
                saveField(filePath, listToJoin(Utils.getFieldList(entry, "fund_list")), "FO");
                saveField(filePath, listToJoin(cnToEnCountry(Utils.getFieldList(entry, "cn_country"), cnEnCountryMap)), "CU");
                saveField(filePath, Integer.toString(Utils.getIntField(entry, "pmId")), "PMID");
                FileUtil.appendUtf8String("ER" + System.lineSeparator(), filePath);
                FileUtil.appendUtf8String(System.lineSeparator(), filePath);
                sumSet.add(aid);
            }
        }
    }

    public static int pmToText(Set<Integer> pmIdSet, String filePath, String bibliometricTable) throws SQLException {
        // 基于bibliometricTable导出的纯文本格式的数据，并返回导出的条数。参数：filePath为保存的路径。pmIdSet为需要导出的pmId号的集合，该集合有可能大于一万篇，所以拆为1000条一次。
        Map<String, String> cnEnCountryMap = cList.getCnEnCountryMap();
        FileUtil.appendUtf8String("FN Clarivate Analytics Web of Science" + System.lineSeparator(), filePath);
        FileUtil.appendUtf8String("VR 1.0" + System.lineSeparator(), filePath);
        Set<Integer> sumSet = new HashSet<>();
        List<Integer> pmIdList = new ArrayList<>();
        for (Integer pmId : pmIdSet) {
            pmIdList.add(pmId);
            if (pmIdList.size() == 1000) {
                readPmId(pmIdList, filePath, bibliometricTable, cnEnCountryMap, sumSet);
                pmIdList.clear();
            }
        }
        if (!pmIdList.isEmpty()) {
            readPmId(pmIdList, filePath, bibliometricTable, cnEnCountryMap, sumSet);
        }
        FileUtil.appendUtf8String("EF", filePath);
        return sumSet.size();
    }

    public static void readPmId(List<Integer> pmIdList, String filePath, String bibliometricTable, Map<String, String> cnEnCountryMap, Set<Integer> sumSet) throws SQLException {
        List<Entity> dataTable = Db.use().query("select `pmid`,`info` from " + bibliometricTable + " where pmid in(" + CollUtil.join(pmIdList, ",") + ");");
        for (Entity entry : dataTable) {
            String info = entry.getStr("info");
            if (null != info && info.contains("authList")) {
                En.TableData tableData = JSONObject.parseObject(info, En.TableData.class);
                saveField(filePath, listToJoin(tableData.getKwPlusList()), "ID");
                saveField(filePath, listToStr(tableData.getKeyword()), "DE");
                saveField(filePath, tableData.getDi(), "DI");
                List<String> auList = new ArrayList<>();
                saveField(filePath, c1AuToJoin(tableData.getC1List(), auList), "C1");
                saveField(filePath, listToJoin(auList), "AU");
                saveField(filePath, c1AuToJoin(tableData.getRpList(), auList), "RP");
                saveField(filePath, tableData.getAbText(), "AB");
                saveField(filePath, listToJoin(tableData.getReferenceList()), "CR");
                saveField(filePath, Integer.toString(tableData.getTotal()), "TC");
                saveField(filePath, tableData.getTitle(), "TI");
                saveField(filePath, listToJoin(tableData.getNcbiSubject()), "WC");
                saveField(filePath, Integer.toString(tableData.getYear()), "PY");
                saveField(filePath, tableData.getLan(), "LA");
                saveField(filePath, tableData.getJournal(), "SO");
                saveField(filePath, listToJoin(new ArrayList<>(tableData.getPtSet())), "DT");
                saveField(filePath, listToJoin(tableData.getFundList()), "FO");
                saveField(filePath, listToJoin(cnToEnCountry(tableData.getCountryList(), cnEnCountryMap)), "CU");
                saveField(filePath, Integer.toString(tableData.getPmId()), "PMID");
                FileUtil.appendUtf8String("ER" + System.lineSeparator(), filePath);
                FileUtil.appendUtf8String(System.lineSeparator(), filePath);
                sumSet.add(tableData.getPmId());
            }
        }
    }

    public static List<String> cnToEnCountry(List<String> cnCountryList, Map<String, String> cnEnCountryMap) {
        // 将中文的国家名称，转换成英文的，并且如果是中文，且无法映射为英文的，直接给予排除。
        List<String> enCountryList = new ArrayList<>();
        for (String cnCountry : cnCountryList) {
            if (cnEnCountryMap.containsKey(cnCountry)) {
                enCountryList.add(cnEnCountryMap.get(cnCountry));
            } else if (!Utils.isContainChinese(cnCountry)) {
                enCountryList.add(cnCountry);
            }
        }
        return enCountryList;
    }

    public static String rpAuToJoin(List<En.AuthListAndInfo> rpList) {
        StringBuilder r = new StringBuilder();
        for (En.AuthListAndInfo authListAndInfo : rpList) {
            List<String> authList = authListAndInfo.getAuthList();
            String full = authListAndInfo.getFull();
            String info = authListAndInfo.getInfo();
            // 如果作者是空，则不添加作者这一部分，包括方括号也不添加。
            if (!authList.isEmpty()) {
                r.append(listToJoin(authList)).append(" (corresponding author), ").append((full.isEmpty() ? info : full).replace("; ", ", ")).append("; ");
            }
        }
        r = new StringBuilder(r.toString().endsWith("; ") ? r.substring(0, r.length() - "; ".length()).trim() : r.toString().trim());
        return r.toString().replaceAll(" +", " ").trim();
    }

    public static String c1AuToJoin(List<En.AuthListAndInfo> c1List, List<String> auList) {
        StringBuilder r = new StringBuilder();
        Set<String> auSet = new HashSet<>();
        for (En.AuthListAndInfo authListAndInfo : c1List) {
            List<String> authList = authListAndInfo.getAuthList();
            // 输出论文的所有不重复的作者，并保留原来的顺序。
            for (String auth : authList) {
                if (!auSet.contains(auth) && auth.trim().length() > 1) {
                    auSet.add(auth);
                    auList.add(auth);
                }
            }
            String full = authListAndInfo.getFull();
            String info = authListAndInfo.getInfo();
            // 如果作者是空，则不添加作者这一部分，包括方括号也不添加。
            if (authList.isEmpty()) {
                r.append((full.isEmpty() ? info : full).replace("; ", ", ")).append("; ");
            } else {
                r.append("[").append(listToJoin(authList)).append("] ").append((full.isEmpty() ? info : full).replace("; ", ", ")).append("; ");
            }
        }
        r = new StringBuilder(r.toString().endsWith("; ") ? r.substring(0, r.length() - "; ".length()).trim() : r.toString().trim());
        return r.toString().replaceAll(" +", " ").trim();
    }

    public static void saveField(String filePath, String str, String field) {
        if (null != str && !str.trim().isEmpty()) {
            FileUtil.appendUtf8String(field + " " + str.trim() + System.lineSeparator(), filePath);
        }
    }

    public static String listToStr(String str) {
        if (null != str && str.contains("\"]")) {
            List<String> list = JSONObject.parseArray(str, String.class);
            return CollUtil.join(list, "; ");
        }
        return str;
    }

    public static String listToJoin(List<String> fundList) {
        StringBuilder r = new StringBuilder();
        // 为防止List内部的元素含有分隔符"; "，先进行一次替换，并删除掉多余的空格。
        if (null != fundList && !fundList.isEmpty()) {
            for (String fund : fundList) {
                fund = fund.replace("; ", ", ");
                r.append(fund).append("; ");
            }
            r = new StringBuilder(r.toString().endsWith("; ") ? r.substring(0, r.length() - "; ".length()).trim() : r.toString().trim());
            r = new StringBuilder(r.toString().replaceAll(" +", " "));
        }
        return r.toString();
    }


}
